#import "drawundo.h"

/*
 * Please refer to external documentation about Draw
 * with Undo for information about what ChangeDetails 
 * are and where they fit in.
 *
 * The ChangeDetail.h and ChangeDetail.m files contain
 * the @interfaces and @implementations for the 11 
 * subclasses of ChangeDetail, as well as for ChangeDetail
 * itself. We grouped all the classes into one pair of 
 * files because the classes are so tiny and their behavior
 * is so similar.
 *
 * ChangeDetail
 *     ArrowChangeDetail
 *     DimensionsChangeDetail
 *     FillColorChangeDetail
 *     FillModeChangeDetail
 *     LineCapChangeDetail
 *     LineColorChangeDetail
 *     LineJoinChangeDetail
 *     LineWidthChangeDetail
 *     MoveChangeDetail
 *     OrderChangeDetail
 * 
 */

@interface ChangeDetail(PrivateMethods)

- (BOOL)personalChangeExpected;

@end

@implementation ChangeDetail

- initGraphic:aGraphic change:aChange
{
    List *subGraphics;
    int count, i;
    id changeDetail;
    
    graphic = aGraphic;
    change = aChange;
    if ([graphic isKindOf:[Group class]] && [self useNestedDetails]) {
        detailsDetails = [[List alloc] init];
	subGraphics = [(Group *)graphic subGraphics];
	count = [subGraphics count];
	changeExpected = NO;
	for (i = 0; i < count; i++) {
	    changeDetail = [[[aChange changeDetailClass] alloc] initGraphic:[subGraphics objectAt:i] change:aChange];
	    changeExpected = changeExpected || [changeDetail changeExpected];
	    [detailsDetails addObject:changeDetail];
	}
    } else {
        detailsDetails = nil;
	changeExpected = [self personalChangeExpected];
    }
    return self;
}

- free
{
    [detailsDetails freeObjects];
    [detailsDetails free];
    return [super free];
}

- graphic
{
    return graphic;
}

- (BOOL)useNestedDetails
{
    return YES;
}

- (BOOL)changeExpected
{
    return changeExpected;
}

- recordDetail
{
    if(detailsDetails)
        [detailsDetails makeObjectsPerform:@selector(recordDetail)];
    else
      [self recordIt];
    return self;
}

- undoDetail
{
    if (detailsDetails)
        [detailsDetails makeObjectsPerform:@selector(undoDetail)];
    else
      [self undoIt];
    return self;
}

- redoDetail
{
    if (detailsDetails)
        [detailsDetails makeObjectsPerform:@selector(redoDetail)];
    else
      [self redoIt];
    return self;
}

- recordIt
{
    /* Implemented by subclasses */
    return self;
}

- undoIt
{
    /* Implemented by subclasses */
    return self;
}

- redoIt
{
    /* Implemented by subclasses */
    return self;
}

- (BOOL)personalChangeExpected
{
    return YES;
}

@end

@implementation ArrowChangeDetail

- recordIt
{
    oldLineArrow = [graphic lineArrow];
    return self;
}

- undoIt
{
    [graphic setLineArrow:oldLineArrow];
    return self;
}

- redoIt
{
    [graphic setLineArrow:[change lineArrow]];
    return self;
}

- (BOOL)personalChangeExpected
{
    return ([graphic lineArrow] != [change lineArrow]);
}

@end

@implementation DimensionsChangeDetail

- (BOOL)useNestedDetails
{
    return NO;
}

- recordDetail
{
    [graphic getBounds:&oldBounds];
    return self;
}

- undoDetail
{
    [graphic getBounds:&newBounds];
    [graphic setBounds:&oldBounds];
    return self;
}

- redoDetail
{
    [graphic setBounds:&newBounds];
    return self;
}

@end

@implementation FillChangeDetail

- recordIt
{
    oldFillColor = [graphic fillColor];
    oldFillMode = [graphic fill];
    return self;
}

- undoIt
{
    newFillColor = [graphic fillColor];
    newFillMode = [graphic fill];
    [graphic setFillColor:&oldFillColor];
    [graphic setFill:oldFillMode];
    return self;
}

- redoIt
{
    [graphic setFillColor:&newFillColor];
    [graphic setFill:newFillMode];
    return self;
}

- (BOOL)personalChangeExpected
{
    return ([graphic fill] != [change fill]);
}

@end

@implementation LineCapChangeDetail

- recordIt
{
    oldLineCap = [graphic lineCap];
    return self;
}

- undoIt
{
    [graphic setLineCap:oldLineCap];
    return self;
}

- redoIt
{
    [graphic setLineCap:[change lineCap]];
    return self;
}

- (BOOL)personalChangeExpected
{
    return ([graphic lineCap] != [change lineCap]);
}

@end

@implementation LineColorChangeDetail

- recordIt
{
    oldColor = [graphic lineColor];
    oldIsOutlined = [graphic isOutlined];
    return self;
}

- undoIt
{
    [graphic setLineColor:&oldColor];
    [graphic setOutlined:oldIsOutlined];
    return self;
}

- redoIt
{
    NXColor color = [change lineColor];
    [graphic setLineColor:&color];
    [graphic setOutlined:YES];
    return self;
}

- (BOOL)personalChangeExpected
{
    return (!NXEqualColor([graphic lineColor], [change lineColor]));
}

@end

@implementation LineJoinChangeDetail

- recordIt
{
    oldLineJoin = [graphic lineJoin];
    return self;
}

- undoIt
{
    [graphic setLineJoin:oldLineJoin];
    return self;
}

- redoIt
{
    [graphic setLineJoin:[change lineJoin]];
    return self;
}

- (BOOL)personalChangeExpected
{
    return ([graphic lineJoin] != [change lineJoin]);
}

@end

@implementation LineWidthChangeDetail

- recordIt
{
    oldLineWidth = [graphic lineWidth];
    return self;
}

- undoIt
{
    [graphic setLineWidth:&oldLineWidth];
    return self;
}

- redoIt
{
    float lineWidth = [change lineWidth];
    [graphic setLineWidth:&lineWidth];
    return self;
}

- (BOOL)personalChangeExpected
{
    return ([graphic lineWidth] != [change lineWidth]);
}

@end

@implementation MoveChangeDetail

- (BOOL)useNestedDetails
{
    return NO;
}

- undoDetail
{
    [graphic moveBy:[change undoVector]];
    return self;
}

- redoDetail
{
    [graphic moveBy:[change redoVector]];
    return self;
}

@end

@implementation OrderChangeDetail

- (BOOL)useNestedDetails
{
    return NO;
}

- recordGraphicPositionIn:graphicList
{
    graphicPosition = [graphicList indexOf:graphic];
    return self;
}

- (unsigned)graphicPosition
{
    return graphicPosition;
}

@end
